from datetime import datetime
from django.shortcuts import render, redirect
from django.views import View
from django import http
from meiduo_mall.utils.log import logger_out
from meiduo_mall.utils.login import LoginRequiredMixin
from .models import User, Address
from django.contrib.auth import login, logout
from meiduo_mall.utils.response_code import RETCODE
from django_redis import get_redis_connection
from django.contrib.auth import authenticate
from .constants import USERNAME_COOKIE_EXPIRES
from celery_tasks.email_active.tasks import send_verify_email
from django.conf import settings
from meiduo_mall.utils import meiduo_signature
from . import constants
from carts.utils import merge_cart_cookie_to_redis

import re
import json


# 对注册页面进行接受，验证，处理，响应
class RegisterView(View):
    def get(self, request):
        # 返回注册页面
        return render(request, "register.html")

    def post(self, request):
        # 获取用户名
        username = request.POST.get("user_name")
        # 获取密码
        password = request.POST.get("pwd")
        # 获取确认密码
        password2 = request.POST.get("cpwd")
        # 获取手机号
        mobile = request.POST.get("phone")
        # 获取短信验证码
        sms_code = request.POST.get("msg_code")
        # 获取是否同意协议
        allow = request.POST.get("allow")

        # 对获取的信息进行判断
        # 判断是否是非空
        if not all([username, password, password2, mobile, sms_code, allow]):
            return http.HttpResponseForbidden("数据不完整！")
        # 对姓名进行格式判断
        if not re.match("^[a-zA-Z0-9_-]{5,20}$", username):
            return http.HttpResponseForbidden("用户名为5-20个字符！")
        # 对姓名进行重名认证
        if User.objects.filter(username=username).count() > 0:
            return http.HttpResponseForbidden("用户名已经存在！")
        # 验证密码
        if not re.match("^[0-9A-Za-z]{8,20}$", password):
            return http.HttpResponseForbidden("密码格式不正确！")
        # 判断前后密码不一致
        if password != password2:
            return http.HttpResponseForbidden("两次输入的密码不一致！")
        # 手机号验证
        if not re.match("^1[3456789]\d{9}$", mobile):
            logger_out.warning("手机号格式有问题！")
            return http.HttpResponseForbidden("手机号格式有问题！")
        # 手机号重复验证
        if User.objects.filter(mobile=mobile).count() > 0:
            logger_out.warning("手机号重复！请换个手机号再测试")
            return http.HttpResponseForbidden("手机号重复！请换个手机号再测试！")

        # 验证手机号是否发送验证码ok
        redis_cli = get_redis_connection("sms_code")
        sms_code_redis = redis_cli.get(mobile)
        if sms_code_redis is None:
            logger_out.warning("手机验证码已经过期！")
            return http.HttpResponseForbidden("手机验证码已经过期！")

        try:
            redis_cli.delete(mobile)
        except Exception as e:
            logger_out.warning(e)

        if sms_code_redis.decode("utf-8") != sms_code:
            logger_out.warning("手机验证码输入错误！")
            return http.HttpResponseForbidden("手机验证码输入错误！")

        # 处理
        # 创建user用户对象
        user = User.objects.create_user(username=username,
                                        password=password,
                                        mobile=mobile)
        # 状态保持，会话保持,向session中保存用户id
        login(request, user)

        # 跳转到主页
        response = redirect('/')

        # 向cookie中写用户名，用于客户端显示
        response.set_cookie("username", username, max_age=USERNAME_COOKIE_EXPIRES)

        # 响应,重定向
        return response


# 接受ajax请求，对注册页面用户名进行重复验证
class UsernameCountView(View):
    def get(self, request, username):
        # 判断用户的统计次数
        count = User.objects.filter(username=username).count()
        return http.JsonResponse({"count": count,
                                  "code": RETCODE.OK,
                                  "errmsg": 'ok'})


# 接受ajax请求，对注册页面手机号进行重复验证
class UserMobileCountView(View):
    def get(self, request, mobile):
        count = User.objects.filter(mobile=mobile).count()
        return http.JsonResponse({"count": count,
                                  "code": RETCODE.OK,
                                  "errmsg": 'ok'})


# 验证用户登录
class LoginView(View):
    def get(self, request):
        return render(request, "login.html")

    def post(self, request):
        # 获取用户名和密码
        username = request.POST.get("username")
        password = request.POST.get("pwd")
        # 判断用户信息
        print(username)
        print(password)
        # 密码和用户帐号是否正确
        # 使用django自带的用户认证
        user = authenticate(request, username=username, password=password)
        if user is None:
            return http.HttpResponseForbidden("用户名或密码错误")
        else:
            # 保持登录会话机制
            login(request, user)
            # 跳转到主页
            response = redirect('/')
            # 向cookie中写用户名，用于客户端显示
            response.set_cookie("username", username, max_age=USERNAME_COOKIE_EXPIRES)
            # 将用户未登录的购物车添加到登录时候的购物车
            response = merge_cart_cookie_to_redis(request, response)
            return response


# 用户登出
class LogOutView(View):
    def get(self, request):
        # 删除状态保持
        logout(request)
        # # 删除cookie中的username，跳转至登录页面
        response = redirect("/login/")
        response.delete_cookie('username')
        return response


# 登录用户到用户中心
class User_Center_InfoView(LoginRequiredMixin, View):
    def get(self, request):
        # 1.利用用户的属性判断
        # if request.user.is_authenticated:
        #     return render(request, "user_center_info.html")
        # else:
        #     return  redirect("/login/")
        # 2. 使用装饰器
        # 3. 利用扩展类来进行判断
        context = {
            'username': request.user.username,
            'mobile': request.user.mobile,
            'email': request.user.email,
            'email_active': request.user.email_active
        }
        return render(request, "user_center_info.html", context)


# 用户中心绑定email
class EmailView(LoginRequiredMixin, View):
    def put(self, request):
        # 接受
        json_dict = json.loads(request.body.decode())
        email = json_dict.get("email")
        # 验证
        if not all([email]):
            return http.JsonResponse({'code': RETCODE.EMAILERR,
                                      'errmsg': '输入的Email无效！'})
        if not re.match(r'^[a-z0-9_][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
            return http.JsonResponse({'code': RETCODE.EMAILERR,
                                      'errmsg': '输入的Email格式错误！'})
        # 处理一：保存email，但是没有验证email
        # try:
        #     user = request.user
        #     user.email_active = email_active
        #     user.save()
        # except Exception as e:
        #     print(e)
        #     return http.JsonResponse({'code': RETCODE.DBERR,
        #                                'errmsg': '添加邮箱失败！'})
        # return http.JsonResponse({'code': RETCODE.OK,
        #                            'errmsg': 'OK'})

        # 处理二：保存并且验证email
        try:
            request.user.email = email
            request.user.save()
        except Exception as e:
            logger_out.warning(e)
            return http.JsonResponse({'code': RETCODE.DBERR,
                                      'errmsg': '添加邮箱失败！'})
        # 异步发送邮箱验证
        # 将用户编号id进行加密
        token = meiduo_signature.dumps({"user_id": request.user.id}, constants.VERF_EMAIL_EXPIRES)
        verify_url = settings.EMAIL_VERIFY_URL + '?token=' + token
        send_verify_email.delay(email, verify_url)
        return http.JsonResponse({'code': RETCODE.OK,
                                  'errmsg': '添加邮箱成功！'})


# 激活email
class EmailActiveView(View):
    def get(self, request):
        # 获取参数token进行验证
        token = request.GET.get('token')
        if not all([token]):
            return http.HttpResponseForbidden("参数无效")
        json_dict = meiduo_signature.loads(token, constants.VERF_EMAIL_EXPIRES)
        if json_dict is None:
            return http.HttpResponseForbidden("激活信息过期！")
        user_id = json_dict.get('user_id')
        try:
            user = User.objects.get(pk=user_id)
        except Exception as e:
            logger_out.warning(e)
            return http.HttpResponseForbidden("用户无效！")
        # 保存激活标志位
        user.email_active = True
        user.save()
        # 响应
        return redirect('/info/')


# 收货地址
class AddressView(LoginRequiredMixin, View):
    def get(self, request):
        # 获取用户列表显示
        # 获取用户名
        login_user = request.user
        # 过滤用户的地址
        addresses = Address.objects.filter(user=login_user, is_deleted=False).order_by('-update_time')
        # 思考如何将最近使用的地址进行呈现到前面的地方，使用order_by
        address_dict_list = []
        # 将用户的地址全部返回给前端。
        for address in addresses:
            address_dict = {
                "id": address.id,
                "title": address.title,
                "receiver": address.receiver,
                "province": address.province.name,
                "province_id": address.province.id,
                "city": address.city.name,
                "city_id": address.city.id,
                "district": address.district.name,
                "district_id": address.district.id,
                "place": address.place,
                "mobile": address.mobile,
                "tel": address.tel,
                "email": address.email
            }
            address_dict_list.append(address_dict)
        context = {
            'default_address_id': login_user.default_address_id,
            'addresses': address_dict_list,
            'user': request.user,
        }
        return render(request, 'user_center_site.html', context)


# 新建保存收货地址
class CreateAddressView(LoginRequiredMixin, View):
    def post(self, request):
        # 首先判断存储的地址是否超过20个,删除的不算
        count = Address.objects.filter(user_id=request.user.id, is_deleted=False).count()
        if count > constants.USER_ADDRESS_COUNT:
            return http.JsonResponse({'code': RETCODE.THROTTLINGERR,
                                      'errmsg': '地址超过设置数量上限'})
        # 验证
        # 获取数据,非表单数据提交
        json_dict = json.loads(request.body.decode())
        # 获取数据
        # * address_id ** string是要修改的地址ID（路径参数）
        # * receiver ** string是收货人 *
        # * province_id ** string是省份ID *
        # * city_id ** string是城市ID *
        # * district_id ** string是区县ID *
        # * place ** string是收货地址 *
        # * mobile  string是手机号 *
        # * tel   string否固定电话 *
        # * email   string否邮箱

        # address_id = json_dict.get('address_id')
        title = json_dict.get('title')
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden({'code': RETCODE.PARAMERR,
                                               'errmsg': '数据不完整'})
        # 验证手机号
        if not re.match(r'^1[345789]\d{9}$', mobile):
            return http.HttpResponseForbidden({'code': RETCODE.PARAMERR,
                                               'errmsg': '移动手机号格式错误'})
        # 验证固定手机号
        if tel:
            if not re.match('^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden({'code': RETCODE.PARAMERR,
                                                   'errmsg': '固定手机号格式错误！'})

        # 验证email
        if email:
            if not re.match('^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden({'code': RETCODE.PARAMERR,
                                                   'errmsg': '邮件的格式错误！'})
        # 处理
        # 保存数据
        try:
            address = Address.objects.create(
                user=request.user,
                title=title,
                receiver=receiver,
                # province_id 是外键连接主键表的id/pk
                # province=Area(pk=province_id),
                # city=Area(pk=city_id),
                # district=Area(pk=district_id),
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email,
            )
            if not request.user.default_address:
                request.user.default_address = address
                request.user.save()
        except Exception as e:
            logger_out.warning(e)
            return http.HttpResponseForbidden({'code': RETCODE.DBERR,
                                               'errmsg': '保存失败！'})
        # 响应
        # 新增地址成功，将新增的地址响应给前端实现局部刷新
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "province_id": address.province_id,
            "city": address.city.name,
            "city_id": address.city_id,
            "district": address.district.name,
            "district_id": address.district_id,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email,
        }
        # 响应保存结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '新增地址成功', 'address': address_dict})


# 编辑收货地址和删除地址
class UpdateDestroyAddressView(LoginRequiredMixin, View):
    def put(self, request, address_id):
        # 验证
        # 获取数据,非表单数据提交
        json_dict = json.loads(request.body.decode())
        # 获取数据
        title = json_dict.get('title')
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden({'code': RETCODE.PARAMERR,
                                               'errmsg': '数据不完整'})
        # 验证手机号
        if not re.match(r'^1[345789]\d{9}$', mobile):
            return http.HttpResponseForbidden({'code': RETCODE.PARAMERR,
                                               'errmsg': '移动手机号格式错误'})
        # 验证固定手机号
        if tel:
            if not re.match('^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden({'code': RETCODE.PARAMERR,
                                                   'errmsg': '固定手机号格式错误！'})

        # 验证email
        if email:
            if not re.match('^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden({'code': RETCODE.PARAMERR,
                                                   'errmsg': '邮件的格式错误！'})
        # 处理
        # 保存数据
        try:
            Address.objects.filter(id=address_id).update(
                user=request.user,
                title=title,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email,
                # update_time=datetime.now(), # 在这里需要utc模型类的简单设置
            )
        except Exception as e:
            logger_out.warning(e)
            return http.HttpResponseForbidden({'code': RETCODE.DBERR,
                                               'errmsg': '更改保存失败！'})
        # 响应
        # 新增地址成功，将新增的地址响应给前端实现局部刷新
        address = Address.objects.get(id=address_id)
        address_dict = {
            'title': address.title,
            "id": address.id,
            "receiver": address.receiver,
            "province": address.province.name,
            "province_id": address.province_id,
            "city": address.city.name,
            "city_id": address.city_id,
            "district": address.district.name,
            "district_id": address.district_id,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email,
        }
        # 响应保存结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '地址更改成功！', 'address': address_dict})

    # 删除地址是软删除，置删除标志为True
    def delete(self, request, address_id):
        try:
            address = Address.objects.get(id=address_id)
            address.is_deleted = True
            address.save()
        except Exception as e:
            logger_out.warning(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '删除地址失败'})
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '删除地址成功！'})


# 设置默认地址
class DefaultAddressView(LoginRequiredMixin, View):
    def put(self, request, address_id):
        try:
            user = request.user
            user.default_address = Address.objects.get(id=address_id)
            user.save()
        except Exception as e:
            logger_out.warning(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '设置默认地址错误'})
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '设置默认地址成功！'})


# 修改title
class UpdateTitleAddressView(LoginRequiredMixin, View):
    def put(self, request, address_id):
        # 接受
        user = request.user
        title = json.loads(request.body.decode('utf-8'))
        # 验证
        if not all([title]):
            return http.JsonResponse({'code': RETCODE.PARAMERR, 'errmsg': "输入的标题为空！"})
        if Address.objects.filter(title=title, user_id=user.id, is_deleted=False).count() > 0:
            return http.JsonResponse({'code': RETCODE.PARAMERR, 'errmsg': "参数已经存在！"})
        try:
            address = Address.objects.get(id=address_id)
            address.title = title
            address.save()
        except Exception as e:
            logger_out.warning(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': "标题保存失败！"})
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': "标题保存成功！"})


# 修改密码
class ChangePasswordView(LoginRequiredMixin, View):
    def get(self, request):
        return render(request, 'user_center_pass.html')

    def post(self, request):
        """实现修改密码逻辑"""
        # 接受参数
        opwd = request.POST.get('old_pwd')
        npwd = request.POST.get('new_pwd')
        cpwd = request.POST.get('new_cpwd')
        # 检验参数是否为空
        if not all([opwd, npwd, cpwd]):
            return http.HttpResponseForbidden('参数不完整')
        try:
            request.user.check_password(opwd)
        except Exception as e:
            logger_out.warning(e)
            return http.HttpResponseForbidden("原始密码错误！")
        # 校验新密码和确认密码
        if not re.match(r'^[0-9A-Za-z]{8,20}$', npwd):
            return http.HttpResponseForbidden('密码最少8位，最长20位')
        if npwd != cpwd:
            return http.HttpResponseForbidden('两次输入的密码不一致')
        # 处理
        try:
            request.user.set_password(npwd)
            request.user.save()
        except Exception as e:
            logger_out.warning(e)
            return http.HttpResponseForbidden('设置密码错误')
        # 响应
        return render(request, 'user_center_pass.html')
